#include "Table.h"
#include <QSqlQuery>
#include <QMetaProperty>
#include <QThread>
#include <QJsonObject>

using namespace QyOrm;
QSqlDatabase Table::db;
static bool callNew = false;
QMap<QString, Table*> Table::hashTable;

static int qRegisterMetaType()
{
    int result = qRegisterMetaType<QyOrm::ForeignField>("QyOrm::ForeignField");
    result = qRegisterMetaType<QyOrm::StringField>("QyOrm::StringField");
    result = qRegisterMetaType<QyOrm::IntegerField>("QyOrm::IntegerField");
    result = qRegisterMetaType<QyOrm::DoubleField>("QyOrm::DoubleField");
    result = qRegisterMetaType<QyOrm::DateTimeField>("QyOrm::DateTimeField");
    result = qRegisterMetaType<QyOrm::BooleanField>("QyOrm::BooleanField");
    return result;
}

Table::Table() : QObject(nullptr)
{
    static int b_qRegisterMetaType = qRegisterMetaType();
    Q_UNUSED(b_qRegisterMetaType);
    if(callNew){
        atHeap = true;
        callNew = false;
    }
}

void Table::initialize()
{
    tableName = this->metaObject()->className();
    for(int i = 1; i < metaObject()->propertyCount(); ++i){
        auto m = metaObject()->property(i);
        QString name = m.name(), typeName = m.typeName();
        if(typeName == "char"){
            QStringList&& para = name.split('_');
            if(para[0] == "primarykey"){
                hashField.value(para[1])->primaryKey = true;
                primaryKey = hashField.value(para[1]);
            }else if(para[0] == "notnull"){
                hashField.value(para[1])->notNull = true;
            }else{
                hashField.value(para[1])->unique = true;
            }
        }else if(typeName == "short"){
            QStringList&& para = name.split('_');
            hashForeign.insert(para[0], para[1]);
        }else{
            Field* field = nullptr;
            if(typeName == "QyOrm::StringField"){
                field = property(m.name()).value<QyOrm::StringField>().self;
            }else if(typeName == "QyOrm::IntegerField"){
                field = property(m.name()).value<QyOrm::IntegerField>().self;
            }else if(typeName == "QyOrm::DoubleField"){
                field = property(m.name()).value<QyOrm::DoubleField>().self;
            }else if(typeName == "QyOrm::DateTimeField"){
                field = property(m.name()).value<QyOrm::DateTimeField>().self;
            }else if(typeName == "QyOrm::BooleanField"){
                field = property(m.name()).value<QyOrm::BooleanField>().self;
            }else{
                field = property(m.name()).value<QyOrm::ForeignField>().self;
            }
            hashField.insert(name, field);
            field->fieldName = name;
            field->table = this;
            listField.append(name);
        }
    }
}

bool Table::update(Field *field)
{
    if(!instance) return false;
    QString sql = QString("update %1 set %2 where %3").arg(tableName).arg(field->execute()).arg(primaryKey->execute());
    return QSqlQuery(database()).exec(sql);
}

bool Table::insert()
{
    if(instance || !atHeap) return false;
    QString values;
    for(QString& fieldName : listField){
        Field* field = hashField.value(fieldName);
        if(field->notNull && field->isNull) return false;
        values += (values.isEmpty() ? "" : ",") + field->select();
    }
    QString sql = QString("insert into %1 values(%2);").arg(tableName).arg(values);
    if(QSqlQuery(database()).exec(sql)){
        return (createInstance() == nullptr);
    }
    return false;
}

bool Table::remove()
{
    QString sql = QString("delete from %1 where %2;").arg(tableName).arg(primaryKey->execute());
    if(QSqlQuery(database()).exec(sql)){
        if(hashTable.contains(instanceKey)){
            delete hashTable.value(instanceKey);
        }
        return true;
    }
    return false;
}

bool Table::create()
{
    QString sql = QString("create table %1(").arg(tableName);
    for(QString& fieldName : listField){
        Field* field = hashField.value(fieldName);
        sql += field->create() + ',';
    }
    for(QString& key : hashForeign.keys()){
        Table* table = dynamic_cast<ForeignField*>(hashField.value(key))->get();
        sql += QString("foreign key(%1) references %2(%3)").arg(key).arg(table->tableName).arg(table->primaryKey->fieldName);
    }
    sql = sql.chopped(1) + ')';
    DF << sql;
    return QSqlQuery(database()).exec(sql);
}

QString Table::toJson()
{
    QString result = "{";
    for(QString& fieldName : listField){
        result += QString("\"%1\":").arg(fieldName);
        Field* field = hashField.value(fieldName);
        if(field->fieldType == FieldType::Foreign){
            result += dynamic_cast<ForeignField*>(field)->get()->toJson();
        }else if(field->fieldType == FieldType::String){
            result += QString("\"%1\"").arg(dynamic_cast<StringField*>(field)->get());
        }else if(field->fieldType == FieldType::Boolean){
            result += (dynamic_cast<BooleanField*>(field)->get() ? "true" : "false");
        }else{
            result += field->select();
        }
        result += ',';
    }
    result[result.size() - 1] = '}';
    return result;
}

QString Table::toJson(QList<Table *> tables)
{
    QString result = "[";
    for(Table* table : tables){
        result += table->toJson() + ',';
    }
    result += ']';
    return result;
}

bool Table::fromJson(QJsonObject *object)
{
    if(object || object->isEmpty()) return false;
    for(QString& fieldName : listField){
        Field* field = hashField.value(fieldName);
        if(object->contains(field->fieldName)){
            field->query(object->value(field->fieldName).toVariant());
        }
    }
    return true;
}

bool Table::isInstance()
{
    return instance;
}

void Table::setFieldValue(const QString &fieldName, const QVariant &value)
{
    Field* field = hashField.value(fieldName, nullptr);
    if(!field) return;
    if(field->fieldType == FieldType::String){
        dynamic_cast<StringField*>(field)->set(value.toString());
    }else if(field->fieldType == FieldType::Integer){
        dynamic_cast<IntegerField*>(field)->set(value.toInt());
    }else if(field->fieldType == FieldType::Double){
        dynamic_cast<DoubleField*>(field)->set(value.toDouble());
    }else if(field->fieldType == FieldType::DateTime){
        dynamic_cast<DateTimeField*>(field)->set(value.toDateTime());
    }else if(field->fieldType == FieldType::Boolean){
        dynamic_cast<BooleanField*>(field)->set(value.toBool());
    }else{
        dynamic_cast<ForeignField*>(field)->set(reinterpret_cast<Table*>(value.toULongLong()));
    }
}

QList<Table *> Table::createTable(const QString &className, const QVariant &var)
{
    Constructor constructor = constructors().value( className );
    if ( constructor == nullptr ) return {};
    return (*constructor)(var);
}

QHash<QString, Table::Constructor> &Table::constructors()
{
    static QHash<QString, Constructor> instance;
    return instance;
}

QSqlDatabase Table::database(bool isDelete)
{
    static QHash<QString, QSqlDatabase> hashThreadSqlDatabase;
    QSqlDatabase tDatabase;
    QString threadId = QString::number(reinterpret_cast<quintptr>(QThread::currentThreadId()));

    if(isDelete){
        hashThreadSqlDatabase[threadId].close();
        return tDatabase;
    }

    if(hashThreadSqlDatabase.contains(threadId)){
        tDatabase = hashThreadSqlDatabase.value(threadId);
        if(!tDatabase.isOpen()){
            tDatabase.open();
        }
    }else{
        tDatabase = QSqlDatabase::cloneDatabase(db, threadId);
        hashThreadSqlDatabase.insert(threadId, tDatabase);
        tDatabase.open();
    }
    return tDatabase;
}

void *Table::operator new(size_t size)
{
    callNew = true;
    return ::operator new(size);
}

Table* Table::createInstance()
{
    instance = true;
    instanceKey = tableName + '_' + primaryKey->select();
    if(hashTable.contains(instanceKey)) return hashTable.value(instanceKey);
    hashTable.insert(instanceKey, this);
    return nullptr;
}

Table *Table::searchInstance(const QString &tableName, const QString &primaryKey, bool isString)
{
    QString instanceKey = tableName + '_' + QString(isString ? "'%1'" : "%1").arg(primaryKey);
    if(hashTable.contains(instanceKey)) return hashTable.value(instanceKey);
    return nullptr;
}

void Table::destoryAllInstance()
{
    for(const QString& key : hashTable.keys()){
        delete hashTable.take(key);
    }
}

Table::~Table()
{
    if(instance){
        hashTable.remove(instanceKey);
    }
}










